import { AI } from './components';
import { entityExists, getEntityComponents, hasComponent, pipe, type Component, type IWorld, type Entity } from '$lib/ecs';
import { describe, getTime } from '$lib';
import { Clock, Global, clockOnlyQuery, clockQuery, getClock, getOnTickActive, processClock } from './clock';
import { Player } from './player';

/**
 * Process all actors as long as possible; for user use cmd
 */
export const simulateWorld = (world: IWorld, cmd) => {
  let processed = false;
  const clock = getClock(world);
  while ((cmd !== undefined || processed) && !Global.gameOver[clock]) {
    [cmd, processed] = stepActors(world, cmd);
    if (processed) {
      processClock(world);
      // now set clock to minimum time
      ++Clock.time[clock];
    }
  }
};

export type Action = (world: IWorld, e: Entity) => any;
export type AIHandler = {target: Component, action: Action};

const aiList: AIHandler[] = [];

export const registerAI = (ai: AIHandler) => {
  aiList.push(ai);
};

const findAIAction = (world: IWorld, e: Entity) => {
  for (let {target, action} of aiList) {
    if (hasComponent(world, target, e))
      return action;
  }
  return undefined;
};

/**
 * Process all actors once (i.e. until actor's clock tick)
 */
export const stepActors = (world, cmd) => {
  const queue = [...clockQuery(world)] //.sort((a, b) => Clock.time[a] - Clock.time[b]);
  let time = getTime(world);
  let i = 0;
  while (i < queue.length) {
    const e = queue[i];
    if (Clock.time[e] > time || !entityExists(world, e)) {
      ++i;
    } else if (hasComponent(world, Player, e) && (cmd === undefined)) {
      return [undefined, false];
    } else if (hasComponent(world, Global, e) ) {
      // it's the clock, skip it
      ++i;
    } else {
      if (hasComponent(world, Player, e)) {
        stepActor(world, e, cmd);
        cmd = undefined;
      } else if (hasComponent(world, AI, e)) {
        const action = findAIAction(world, e);
        if (action) {
          stepActor(world, e, action);
        } else {
          console.warn(`AI not found for ${describe(world, e)}`);
          Clock.time[e] += 1;
        }
      } else {
        for (let component of getEntityComponents(world, e)) {
          const action = getOnTickActive(component);
          if (action) {
            action(world, e);
          }
        }
        if (Clock.time[e] <= time) {
          // re-enable this warning when rotting is implemented in other manner
          // console.warn(`Clocked entity doesn't have any action, force ticking; ${describe(world, e)}`);
          Clock.time[e] = time+1;
        }
      }
    }
  }
  // log('tick');
  return [cmd, true];
};

const stepActor = (world, e, cmd) => {
  cmd(world, e);
};
